<?php

class LobbyModel extends Model
{
	public function GetGames()
	{
		require_once 'obj/Game.php';
		require_once 'obj/User.php';
		require_once 'obj/Player.php';
		require_once 'obj/Robot.php';
		require_once 'obj/Map.php';
		require_once 'obj/TurnData.php';

		$games = array();

		$stmt = $this->db->Prepare( "SELECT Games.GameID, Users.UserID, Users.Username, Users.Email, Games.RandomSeed, Games.MaxPlayers, Games.Version, Games.GameModeID, Games.TurnResponseTimer, Games.TurnTimer, Games.Finished, Games.Password, Games.Started, Turns.Turn, Maps.MapID, Maps.Width, Maps.Height, Maps.Tiles, Maps.Name
										FROM Games
											INNER JOIN Users ON Users.UserID = Games.CreatorID
											INNER JOIN Turns ON Turns.GameID = Games.GameID AND Turns.Turn = ( SELECT MAX( Turn ) FROM Turns WHERE GameID = Games.GameID )
											INNER JOIN Maps ON Maps.MapID = Games.MapID
											INNER JOIN GameParticipants AS GP ON GP.GameID = Games.GameID
											GROUP BY Games.GameID" );

		$stmt->execute();
		$stmt->bind_result( $row_gameID, $row_creatorID, $row_creatorName, $row_creatorEmail, $row_randomSeed, $row_maxPlayers, $row_version, $row_gameModeID, $row_turnResponseTimer, $row_turnTimer, $row_finished, $row_password, $row_started, $row_turn, $row_mapID, $row_mapWidth, $row_mapHeight, $row_mapTiles, $row_mapName );

		if ( $db2 = $this->db->OpenNew() )
		{
			while ( $stmt->fetch() )
			{
				$stmt2 = $db2->Prepare( "SELECT Users.UserID, Users.Username, Users.Email, GP.StillPlaying, GP.FinishedTurn, RobotData.PositionX, RobotData.PositionY, RobotData.RespawnPosX, RobotData.RespawnPosY, RobotData.Direction, RobotData.RobotTypeID, RobotData.HP, RobotData.Lives, RobotData.Alive, RobotData.LockedRegisterCards, RobotData.BonusCards, RobotData.RobotDataID, Cards.Cards, TD.TurnDataID, TD.Register1, TD.Register2, TD.Register3, TD.Register4, TD.Register5, TD.PowerDown, TD.SpawnLogic
											FROM GameParticipants AS GP
												INNER JOIN Users ON Users.UserID = GP.UserID
												INNER JOIN RobotData ON RobotData.GameID = GP.GameID AND RobotData.UserID = Users.UserID AND RobotData.RobotDataID = ( SELECT MAX( RobotDataID ) FROM RobotData WHERE GameID = GP.GameID AND UserID = Users.UserID )
												LEFT OUTER JOIN GameCardsDealed AS Cards ON Cards.GameID = GP.GameID AND Cards.UserID = Users.UserID AND Cards.Turn = ( SELECT MAX( Turn ) FROM Turns WHERE GameID = GP.GameID )
												LEFT OUTER JOIN TurnData AS TD ON TD.GameID = GP.GameID AND TD.Turn = ( SELECT MAX( Turn ) FROM Turns WHERE GameID = GP.GameID ) AND TD.UserID = Users.UserID
											WHERE GP.GameID = ?" );
				$stmt2->bind_param( "i", $row_gameID );
				$stmt2->execute();
				$stmt2->bind_result( $row_userID, $row_username, $row_email, $row_stillPlaying, $row_finishedTurn, $row_posX, $row_posY, $row_respawnPosX, $row_respawnPosY, $row_direction, $row_robotTypeID, $row_hp, $row_lives, $row_alive, $row_lockedRegisterCards, $row_bonusCards, $row_robotDataID, $row_cards, $row_turnDataID, $row_register1, $row_register2, $row_register3, $row_register4, $row_register5, $row_powerDown, $row_spawnLogic );
				$players = array();
				while ( $stmt2->fetch() )
				{
					$players[] = new Player( new User( $row_userID, $row_username, $row_email ), new Robot( $row_robotDataID, $row_posX, $row_posY, $row_respawnPosX, $row_respawnPosY, $row_direction, $row_robotTypeID, $row_hp, $row_lives, $row_alive, $row_lockedRegisterCards, $row_bonusCards ), $row_cards, $row_stillPlaying, $row_finishedTurn, new TurnData( $row_turnDataID, $row_register1, $row_register2, $row_register3, $row_register4, $row_register5, $row_powerDown, $row_spawnLogic ) );
				}

				$games[$row_gameID] = new Game( $row_gameID, new User( $row_creatorID, $row_creatorName, $row_creatorEmail ), $row_randomSeed, $row_maxPlayers, $row_version, $row_gameModeID, $row_turnResponseTimer, $row_turnTimer, $row_finished, $row_password, $row_started, $row_turn, new Map( $row_mapID, $row_mapWidth, $row_mapHeight, $row_mapTiles, $row_mapName ), $players );

				$stmt2->close();
			}

			$db2->Close();
		}

		$stmt->close();

		return $games;
	}

	public function FilterGames( $games, $filter )
	{
		 $filteredGames = array();
		 foreach ( $games as $gameID => $game )
		 {
		 	$passFilter = true;


		 	if ( array_key_exists( 'creator', $filter ) && $game->Creator()->ID() != $filter['creator'] )
		 	{
		 		$passFilter = false;
		 	}

		 	if ( array_key_exists( 'player', $filter ) )
		 	{
		 		$hasPlayer = false;
		 		foreach ( $game->Players() as $player )
		 		{
		 			if ( $player->User()->ID() == $filter['player'] )
		 			{
		 				$hasPlayer = true;
		 			}
		 		}

		 		if ( !$hasPlayer )
		 		{
		 			$passFilter = false;
		 		}
		 	}

		 	if ( $passFilter )
		 	{
		 		$filteredGames[$gameID] = $game;
		 	}
		 }

		 return $filteredGames;
	}

	public function GetMaps()
	{
		require_once 'obj/Map.php';
		
		$maps = array();

		$stmt = $this->db->Prepare( "SELECT MapID, Width, Height, Tiles, Name FROM Maps" );
		$stmt->execute();
		$stmt->bind_result( $row_mapID, $row_width, $row_height, $row_tiles, $row_name );

		while ( $stmt->fetch() )
		{
			$maps[] = new Map( $row_mapID, $row_width, $row_height, $row_tiles, $row_name );
		}

		$stmt->close();
		
		return $maps;
	}

	public function CreateGame( $creatorID, $maxPlayers, $mapID )
	{
		// Game Data.
		$randomSeed = mt_rand( 0, 2147483647 );
		$version = 1.1;
		$gameModeID = 1;
		$turnResponseTimer = 1440;
		$turnTimer = 90000;
		$finished = 0;
		$password = "";

		// GameParticipant Data.
		$stillPlaying = 1;
		$finishedTurn = 0;

		// Robot Data.
		$robotTypeID = 1;
		$positionX = 0;
		$positionY = 0;
		$hp = 10;
		$lives = 3;
		$alive = 1;
		$respawnPosX = 0;
		$respawnPosY = 0;
		$lockedRegisterCards = '';
		$bonusCards = '';
		$direction = 1;


		$this->db->StartTransaction();

		// Add Game.
		$stmt = $this->db->Prepare( "INSERT INTO Games ( CreatorID, RandomSeed, MaxPlayers, Version, GameModeID, TurnResponseTimer, TurnTimer, Finished, Password, MapID )
										VALUES( ?, ?, ?, ?, ?, ?, ?, ?, HEX( ? ), ? )" );
		$stmt->bind_param( "iiidiiiisi", $creatorID, $randomSeed, $maxPlayers, $version, $gameModeID, $turnResponseTimer, $turnTimer, $finished, $password, $mapID );
		$this->db->Execute( $stmt );
		$stmt->close();

		// Add the creator as a Participant.
		$gameID = $this->db->LastInsertID();

		$stmt = $this->db->Prepare( "INSERT INTO GameParticipants ( GameID, UserID, StillPlaying, FinishedTurn ) VALUES ( ?,?,?,? )" );
		$stmt->bind_param( "iiii", $gameID, $creatorID, $stillPlaying, $finishedTurn );
		$this->db->Execute( $stmt );
		$stmt->close();

		// Add the creators Robot.
		$stmt = $this->db->Prepare( "INSERT INTO RobotData ( GameID, UserID, RobotTypeID, PositionX, PositionY, HP, Lives, Alive, RespawnPosX, RespawnPosY, LockedRegisterCards, BonusCards, Direction )
													VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
		$stmt->bind_param( "iiiiiiiiiissi", $gameID, $creatorID, $robotTypeID, $positionX, $positionY, $hp, $lives, $alive, $respawnPosX, $respawnPosY, $lockedRegisterCards, $bonusCards, $direction );
		$this->db->Execute( $stmt );
		$stmt->close();

		// Add first turn.
		$stmt = $this->db->Prepare( "INSERT INTO Turns ( Turn, GameID, Turns.Date, TurnFinished ) VALUES ( 1, ?, NOW(), 0 )" );
		$stmt->bind_param( "i", $gameID );
		$this->db->Execute( $stmt );
		$stmt->close();

		if ( !$this->db->EndTransaction() )
		{
			throw new Exception( 'Could not create game.' );
		}
	}

	public function JoinGame( $gameID, $userID )
	{
		// GameParticipant Data.
		$stillPlaying = 1;
		$finishedTurn = 0;

		// Robot Data.
		$robotTypeID = 1;
		$positionX = 0;
		$positionY = 0;
		$hp = 10;
		$lives = 3;
		$alive = 1;
		$respawnPosX = 0;
		$respawnPosY = 0;
		$lockedRegisterCards = '';
		$bonusCards = '';
		$direction = 1;


		$stmt = $this->db->Prepare( "SELECT Games.MaxPlayers, COUNT( GP.UserID ) AS Slots FROM Games INNER JOIN GameParticipants AS GP ON GP.GameID = Games.GameID WHERE Games.GameID = ?" );
		$stmt->bind_param( "i", $gameID );
		$stmt->execute();
		$stmt->bind_result( $row_MaxPlayers, $row_UserCount );
		$stmt->fetch();
		$openSlots = ( $row_UserCount < $row_MaxPlayers );
		$stmt->close();

		if ( !$openSlots )
		{
			throw new Exception( 'The game is already full.' );
		}

		$stmt = $this->db->Prepare( "SELECT COUNT( UserID ) FROM GameParticipants WHERE GameID = ? AND UserID = ?" );
		$stmt->bind_param( "ii", $gameID, $userID );
		$stmt->execute();
		$stmt->bind_result( $row_usercount );
		$stmt->fetch();
		$playerExists = ( $row_usercount > 0 );
		$stmt->close();

		if ( $playerExists )
		{
			throw new Exception( 'You have already joined this game.' );
		}

		$this->db->StartTransaction();

		// Add the player as a Participant.
		$stmt = $this->db->Prepare( "INSERT INTO GameParticipants ( GameID, UserID, StillPlaying, FinishedTurn ) VALUES ( ?,?,?,? )" );
		$stmt->bind_param( "iiii", $gameID, $userID, $stillPlaying, $finishedTurn );
		$this->db->Execute( $stmt );
		$stmt->close();

		// Add the players Robot.
		$stmt = $this->db->Prepare( "INSERT INTO RobotData ( GameID, UserID, RobotTypeID, PositionX, PositionY, HP, Lives, Alive, RespawnPosX, RespawnPosY, LockedRegisterCards, BonusCards, Direction )
													VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
		$stmt->bind_param( "iiiiiiiiiissi", $gameID, $userID, $robotTypeID, $positionX, $positionY, $hp, $lives, $alive, $respawnPosX, $respawnPosY, $lockedRegisterCards, $bonusCards, $direction );
		$this->db->Execute( $stmt );
		$stmt->close();

		if ( !$this->db->EndTransaction() )
		{
			throw new Exception( 'Could not join the game.' );
		}
	}

	public function LeaveGame( $gameID, $userID )
	{
		$stmt = $this->db->Prepare( "SELECT Started FROM Games WHERE GameID = ?" );
		$stmt->bind_param( "i", $gameID );
		$this->db->Execute( $stmt );
		$stmt->bind_result( $row_started );
		$stmt->fetch();
		$started = $row_started;
		$stmt->close();

		if ( $started )
		{
			throw new Exception( 'Can not leave a game in progress.' );
		}


		$this->db->StartTransaction();

		$stmt = $this->db->Prepare( "DELETE FROM GameParticipants WHERE GameID = ? AND UserID = ?" );
		$stmt->bind_param( "ii", $gameID, $userID );
		$this->db->Execute( $stmt );
		$stmt->close();

		$stmt = $this->db->Prepare( "DELETE FROM RobotData WHERE GameID = ? AND UserID = ?" );
		$stmt->bind_param( "ii", $gameID, $userID );
		$this->db->Execute( $stmt );
		$stmt->close();

		if ( !$this->db->EndTransaction() )
		{
			throw new Exception( 'Could not leave the game.' );
		}
	}

	public function DeleteGame( $gameID, $userID )
	{
		$stmt = $this->db->Prepare( "SELECT Started FROM Games WHERE GameID = ?" );
		$stmt->bind_param( "i", $gameID );
		$this->db->Execute( $stmt );
		$stmt->bind_result( $row_started );
		$stmt->fetch();
		$started = $row_started;
		$stmt->close();

		if ( $started )
		{
			throw new Exception( 'Can not remove a game in progress.' );
		}

		$this->db->StartTransaction();

		$stmt = $this->db->Prepare( "DELETE FROM Games WHERE GameID = ? AND CreatorID = ?" );
		$stmt->bind_param( "ii", $gameID, $userID );
		$this->db->Execute( $stmt );
		$stmt->close();

		$stmt = $this->db->Prepare( "DELETE FROM GameParticipants WHERE GameID = ( SELECT Games.GameID FROM Games WHERE Games.CreatorID = ? AND Games.GameID = ? )" );
		$stmt->bind_param( "ii", $userID, $gameID );
		$this->db->Execute( $stmt );
		$stmt->close();	

		$stmt = $this->db->Prepare( "DELETE FROM RobotData WHERE GameID = ( SELECT Games.GameID FROM Games WHERE Games.CreatorID = ? AND Games.GameID = ? )" );
		$stmt->bind_param( "ii", $userID, $gameID );
		$this->db->Execute( $stmt );
		$stmt->close();

		$stmt = $this->db->Prepare( "DELETE FROM Turns WHERE GameID = ( SELECT Games.GameID FROM Games WHERE Games.CreatorID = ? AND Games.GameID = ? )" );
		$stmt->bind_param( "ii", $userID, $gameID );
		$this->db->Execute( $stmt );
		$stmt->close();

		if ( !$this->db->EndTransaction() )
		{
			throw new Exception( 'Could not delete the game.' );
		}
	}

	public function RemovePlayer( $gameID, $userID, $playerID )
	{
		$stmt = $this->db->Prepare( "SELECT Started FROM Games WHERE GameID = ?" );
		$stmt->bind_param( "i", $gameID );
		$this->db->Execute( $stmt );
		$stmt->bind_result( $row_started );
		$stmt->fetch();
		$started = $row_started;
		$stmt->close();

		if ( $started )
		{
			throw new Exception( 'Can not remove players from a game in progress.' );
		}


		$this->db->StartTransaction();

		$stmt = $this->db->Prepare( "DELETE FROM GameParticipants WHERE GameID = ( SELECT Games.GameID FROM Games WHERE Games.CreatorID = ? AND Games.GameID = ? ) AND UserID = ?" );
		$stmt->bind_param( "iii", $userID, $gameID, $playerID );
		$this->db->Execute( $stmt );
		$stmt->close();

		$stmt = $this->db->Prepare( "DELETE FROM RobotData WHERE GameID = ( SELECT Games.GameID FROM Games WHERE Games.CreatorID = ? AND Games.GameID = ? ) AND UserID = ?" );
		$stmt->bind_param( "iii", $userID, $gameID, $playerID );
		$this->db->Execute( $stmt );
		$stmt->close();

		if ( !$this->db->EndTransaction() )
		{
			throw new Exception( 'Could not remove the player.' );
		}
	}

	public function StartGame( $gameID, $userID )
	{
		$this->db->StartTransaction();

		$stmt = $this->db->Prepare( "UPDATE Games SET Started = 1 WHERE GameID = ? AND CreatorID = ?" );
		$stmt->bind_param( "ii", $gameID, $userID );
		$this->db->Execute( $stmt );
		$stmt->close();

		if ( !$this->db->EndTransaction() )
		{
			throw new Exception( 'Could not start the game.' );
		}

		require_once 'php/lib/CardDealer.php';
		$games = $this->GetGames( $gameID );
		$dealer = new CardDealer( $games[ $gameID ] );
		$dealer->DealCards( $this->db );
	}
}
